package pe.com.casadesign.dao.impl;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;

import javax.sql.DataSource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.jdbc.core.simple.SimpleJdbcCall;
import org.springframework.stereotype.Repository;

import pe.com.casadesign.dao.PrestamoDAO;
import pe.com.casadesign.domain.Cliente;
import pe.com.casadesign.domain.Diseniador;
import pe.com.casadesign.domain.Item;
import pe.com.casadesign.domain.ListaItem;
import pe.com.casadesign.domain.ListaPrestamo;
import pe.com.casadesign.domain.Prestamo;
import pe.com.casadesign.domain.Producto;
import pe.com.casadesign.domain.Usuario;
import pe.com.casadesign.domain.Venta;
import pe.com.casadesign.domain.Moneda;
import pe.com.casadesign.domain.Tienda;

/**
 * Implementacion de PrestamoDAO.
 * 
 * @author Pedro Vera
 * 
 */
@Repository
public class PrestamoDAOImpl implements PrestamoDAO {
	private static final Logger logger = LoggerFactory
			.getLogger(PrestamoDAOImpl.class);

	// Nombre Procedimientos Almacenados
	private static String SP_LISTAR_PRESTAMO = "SP_LISTAR_PRESTAMO";
	private static String SP_OBTENER_PRESTAMO = "SP_OBTENER_PRESTAMO";
	private static String SP_REGISTRAR_PRESTAMO = "SP_REGISTRAR_PRESTAMO";
	private static String SP_REGISTRAR_DET_PRESTAMO = "SP_REGISTRAR_DET_PRESTAMO";
	private static String SP_ACTUALIZAR_ESTADO_ITEM = "SP_ACTUALIZAR_ESTADO_ITEM";
	private static String SP_MODIFICAR_PRESTAMO = "SP_MODIFICAR_PRESTAMO";
	private static String  SP_ACTUALIZAR_PRESTAMO = "SP_ACTUALIZAR_PRESTAMO";

	// Parametros de Entrada
	private static String PARAM_CODPRESTAMO = "p_codprestamo";
	private static String PARAM_CODCLIENTE = "p_codcliente";
	private static String PARAM_PERIODOXDIAS = "p_periodoxdias";
	private static String PARAM_FECHAINICIO = "p_fechainicio";
	private static String PARAM_FECHAFIN = "p_fechafin";
	private static String PARAM_CODUSUARIO = "p_codusuario";
	private static String PARAM_ESTADO = "p_estado";
	private static String PARAM_CODDISENIADOR = "p_coddiseniador";
	private static String PARAM_CODTIENDA = "p_codtienda";

	private static String PARAM_ITEM_CODITEM = "p_coditem";
	private static String PARAM_ITEM_ESTADO = "p_estado";

	// Parametros de Salida
	private static String CODPRESTAMO = "CODPRESTAMO";
	private static String CODCLIENTE = "CODCLIENTE";
	private static String CODUSUARIO = "CODUSUARIO";
	private static String ESTADO = "ESTADO";
	private static String CODTIENDA = "CODTIENDA";
	private static String FECHAREGISTRO = "FECHAREGISTRO";
    private static String NOMBRETIENDA = "NOMBRETIENDA";
    private static String TIPODOCUMENTO = "TIPODOCUMENTO";
    private static String PERIODOXDIAS = "PERIODOXDIAS";
    private static String FECHAINICIO = "FECHAINICIO";
    private static String FECHAFIN = "FECHAFIN";
    private static String CODDISENIADOR = "CODDISENIADOR";
    private static String DISENIADORNOMBRESYAPELLIDOS = "DISNOMBRESYAPELLIDOS";
	
	private static String CLIENTE_NRODOCUMENTO = "NRODOCUMENTO";
	private static String CLIENTE_RAZONSOCIAL = "RAZONSOCIAL";

	private static String USUARIO_NOMBRESYAPELLIDOS = "NOMBRESYAPELLIDOS";

	private static String ITEM_CODITEM = "CODITEM";
	private static String ITEM_CODIFICACION = "CODIFICACION";
	private static String ITEM_ESTADO = "ESTADO";
	private static String ITEM_PRECIOCOMPRA = "PRECIOCOMPRA";
	private static String ITEM_CODMONEDA = "CODMONEDA";
	private static String ITEM_CODTIENDA = "CODTIENDA";

	private static String PRODUCTO_CODPRODUCTO = "CODPRODUCTO";
	private static String PRODUCTO_CODPRODUCTOPROVEEDOR = "CODPRODUCTOPROVEEDOR";
	private static String PRODUCTO_DESCRIPCION = "DESCRIPCION";

	private static String LISTADO = "LISTADO";
	private static String LISTADO_LINEAS = "LISTADO_LINEAS";

	private static String TOTAL_FILAS_AFECTADAS = "#update-count-1";

	private JdbcTemplate jdbcTemplate;

	@Autowired
	public void setDataSource(DataSource dataSource) {
		this.jdbcTemplate = new JdbcTemplate(dataSource);
	}

	@Override
	public ListaPrestamo listarPrestamo(Prestamo prestamo) {
		logger.info("listarPrestamo");

		// Instanciando variable para llamado de SP
		SimpleJdbcCall llamarSP = new SimpleJdbcCall(jdbcTemplate)
				.withProcedureName(SP_LISTAR_PRESTAMO).returningResultSet(
						LISTADO, new RowMapper<Prestamo>() {
							@Override
							public Prestamo mapRow(final ResultSet rs,
									final int numRow) throws SQLException {
								final Prestamo prestamo = new Prestamo();
								prestamo.setCodPrestamo(rs.getInt(CODPRESTAMO));

								Cliente cliente = new Cliente();
								cliente.setCodCliente(rs.getInt(CODCLIENTE));
								cliente.setTipoDocumento(rs.getString(TIPODOCUMENTO));
								cliente.setNroDocumento(rs.getString(CLIENTE_NRODOCUMENTO));
								cliente.setRazonSocial(rs.getString(CLIENTE_RAZONSOCIAL));
								prestamo.setCliente(cliente);
								
								Tienda tienda = new Tienda();
								tienda.setCodigo(rs.getInt(CODTIENDA));
								//tienda.setDescripcion(rs.getString(NOMBRETIENDA));
								prestamo.setTienda(tienda);

								Usuario usuario = new Usuario();
								usuario.setCodUsuario(rs.getInt(CODUSUARIO));
								usuario.setNombresyApellidos(rs.getString(USUARIO_NOMBRESYAPELLIDOS));
								prestamo.setUsuario(usuario);
								
								Diseniador diseniador = new Diseniador();
								diseniador.setCodDiseniador(rs.getInt(CODDISENIADOR));
								diseniador.setNombresyApellidos(rs.getString(DISENIADORNOMBRESYAPELLIDOS));
								prestamo.setDiseniador(diseniador);

								prestamo.setPeriodoxDias(rs.getInt(PERIODOXDIAS));
								prestamo.setFechaInicio(rs.getDate(FECHAINICIO).toString());
								prestamo.setFechaFin(rs.getDate(FECHAFIN).toString());
								prestamo.setEstado(rs.getString(ESTADO));
								prestamo.setFechaRegistro(rs.getDate(FECHAREGISTRO).toString());
								return prestamo;
							}
						});

		// Estableciendo parametros de entrada al SP
		SqlParameterSource parametros = new MapSqlParameterSource()
				.addValue(PARAM_CODPRESTAMO, prestamo.getCodPrestamo())
				.addValue(PARAM_CODCLIENTE,prestamo.getCliente().getCodCliente())
				.addValue(PARAM_CODTIENDA,prestamo.getTienda().getCodigo())
				.addValue(PARAM_ESTADO, prestamo.getEstado());

		// Establecer parametros y ejecutar SP
		Map<String, Object> data = llamarSP.execute(parametros);

		// Obteniendo lista generada de ejecutar SP
		@SuppressWarnings("unchecked")
		List<Prestamo> listaPrestamo = (List<Prestamo>) data.get(LISTADO);

		// Instanciando objeto ListaPrestamo
		ListaPrestamo listaPrestamoResultado = new ListaPrestamo();
		// Estableciendo resultado al objeto listaPrestamoResultado
		listaPrestamoResultado.setLista(listaPrestamo);

		if (logger.isDebugEnabled() && listaPrestamoResultado != null) {
			logger.debug("size=" + listaPrestamoResultado.size());
		}

		return listaPrestamoResultado;
	}

	@Override
	public Prestamo obtenerPrestamo(Prestamo prestamo) {
		logger.info("obtenerPrestamo");

		// Instanciando variable para llamado de SP
		SimpleJdbcCall llamarSP = new SimpleJdbcCall(jdbcTemplate)
				.withProcedureName(SP_OBTENER_PRESTAMO)
				.returningResultSet(LISTADO, new RowMapper<Prestamo>() {
					@Override
					public Prestamo mapRow(final ResultSet rs, final int numRow)
							throws SQLException {
						final Prestamo prestamo = new Prestamo();
						prestamo.setCodPrestamo(rs.getInt(CODPRESTAMO));

						Cliente cliente = new Cliente();
						cliente.setCodCliente(rs.getInt(CODCLIENTE));
						cliente.setTipoDocumento(rs.getString(TIPODOCUMENTO));
						cliente.setNroDocumento(rs.getString(CLIENTE_NRODOCUMENTO));
						cliente.setRazonSocial(rs.getString(CLIENTE_RAZONSOCIAL));
						prestamo.setCliente(cliente);
						
						Tienda tienda = new Tienda();
						tienda.setCodigo(rs.getInt(CODTIENDA));
						//tienda.setDescripcion(rs.getString(NOMBRETIENDA));
						prestamo.setTienda(tienda);

						Usuario usuario = new Usuario();
						usuario.setCodUsuario(rs.getInt(CODUSUARIO));
						usuario.setNombresyApellidos(rs.getString(USUARIO_NOMBRESYAPELLIDOS));
						prestamo.setUsuario(usuario);
						
						Diseniador diseniador = new Diseniador();
						diseniador.setCodDiseniador(rs.getInt(CODDISENIADOR));
						diseniador.setNombresyApellidos(rs.getString(DISENIADORNOMBRESYAPELLIDOS));
						prestamo.setDiseniador(diseniador);

						prestamo.setEstado(rs.getString(ESTADO));
						prestamo.setPeriodoxDias(rs.getInt(PERIODOXDIAS));
						prestamo.setFechaInicio(rs.getDate(FECHAINICIO).toString());
						prestamo.setFechaFin(rs.getDate(FECHAFIN).toString());
						prestamo.setFechaRegistro(rs.getDate(FECHAREGISTRO).toString());
						return prestamo;
					}
				}).returningResultSet(LISTADO_LINEAS, new RowMapper<Item>() {
					@Override
					public Item mapRow(final ResultSet rs, final int numRow)
							throws SQLException {
						final Item item = new Item();
						item.setCodItem(rs.getInt(ITEM_CODITEM));
						item.setCodificacion(rs.getString(ITEM_CODIFICACION));
						item.setEstado(rs.getString(ITEM_ESTADO));
						item.setPrecioCompra(rs.getDouble(ITEM_PRECIOCOMPRA));

						Moneda moneda = new Moneda();
						moneda.setCodigo(rs.getInt(ITEM_CODMONEDA));
						item.setMoneda(moneda);

						Tienda tienda = new Tienda();
						tienda.setCodigo(rs.getInt(ITEM_CODTIENDA));
						item.setTienda(tienda);

						Producto producto = new Producto();
						producto.setCodigo(rs.getInt(PRODUCTO_CODPRODUCTO));
						producto.setCodProductoProveedor(rs.getString(PRODUCTO_CODPRODUCTOPROVEEDOR));
						producto.setDescripcion(rs.getString(PRODUCTO_DESCRIPCION));
						item.setProducto(producto);

						return item;
					}
				});

		// Estableciendo parametros de entrada al SP
		SqlParameterSource parametros = new MapSqlParameterSource()
		        .addValue(PARAM_CODPRESTAMO, prestamo.getCodPrestamo());

		// Establecer parametros y ejecutar SP
		Map<String, Object> data = llamarSP.execute(parametros);

		// Obteniendo lista generada de ejecutar SP
		@SuppressWarnings("unchecked")
		List<Prestamo> listaPrestamo = (List<Prestamo>) data.get(LISTADO);

		// Declarando objeto prestamo Obtenido
		Prestamo prestamoObtenido = null;

		if (logger.isDebugEnabled() && listaPrestamo != null) {
			logger.debug("size=" + listaPrestamo.size());
		}

		if (listaPrestamo != null && listaPrestamo.size() > 0) {
			// Obteniendo el primer elemento de la lista de prestamo
			prestamoObtenido = listaPrestamo.get(0);

			// Obteniendo lista de lineas generada de ejecutar SP
			@SuppressWarnings("unchecked")
			List<Item> listaItem = (List<Item>) data.get(LISTADO_LINEAS);

			// Instanciando objeto ListaItem
			ListaItem listaItemResultado = new ListaItem();
			// Estableciendo resultado al objeto listaItemResultado
			listaItemResultado.setLista(listaItem);

			// Asignando lista de lineas al objeto prestamo creado
			prestamoObtenido.setListaItem(listaItemResultado);

			if (logger.isDebugEnabled() && listaItemResultado != null) {
				logger.debug("sizeLineas=" + listaItemResultado.size());
			}
		}

		return prestamoObtenido;
	}

	@Override
	public boolean registrarPrestamo(Prestamo prestamo) {
		logger.info("registrarPrestamo");

		int resultado = 0;

		// Instanciando variable para llamado de SP
		SimpleJdbcCall llamarSP = new SimpleJdbcCall(jdbcTemplate)
				.withProcedureName(SP_REGISTRAR_PRESTAMO);

		// Estableciendo parametros de entrada al SP
		SqlParameterSource parametros = new MapSqlParameterSource()
				.addValue(PARAM_CODCLIENTE,	prestamo.getCliente().getCodCliente())
				.addValue(PARAM_PERIODOXDIAS, prestamo.getPeriodoxDias())
				.addValue(PARAM_FECHAINICIO, prestamo.getFechaInicio())
				.addValue(PARAM_FECHAFIN, prestamo.getFechaFin())
				.addValue(PARAM_CODUSUARIO,	prestamo.getUsuario().getCodUsuario())
				.addValue(PARAM_CODTIENDA, prestamo.getTienda().getCodigo())
				.addValue(PARAM_CODDISENIADOR,prestamo.getDiseniador()==null?null:prestamo.getDiseniador().getCodDiseniador())
				.addValue(PARAM_ESTADO, prestamo.getEstado());

		// Establecer parametros y ejecutar SP
		Map<String, Object> data = llamarSP.execute(parametros);

		// Obteniendo el total de filas afectadas al ejecutar SP
		resultado += (Integer) data.get(TOTAL_FILAS_AFECTADAS);

		logger.debug("codigo generado:" + data.get(PARAM_CODPRESTAMO));

		// Obteniendo codigo de prestamo
		int codigoPrestamo = (Integer) data.get(PARAM_CODPRESTAMO);

		// Estableciendo codigo generado
		prestamo.setCodPrestamo(codigoPrestamo);

		// Obteniendo lista de item
		ListaItem listaItem = prestamo.getListaItem();

		// Si existen items entonces asociarlos
		if (listaItem != null && listaItem.getLista() != null) {
			// Instanciando variable para llamado de SP
			SimpleJdbcCall llamarSPLineas = new SimpleJdbcCall(jdbcTemplate)
					.withProcedureName(SP_REGISTRAR_DET_PRESTAMO);

			for (Item item : listaItem.getLista()) {
				// Estableciendo parametros de entrada al SP
				SqlParameterSource parametrosDetalle = new MapSqlParameterSource()
						.addValue(PARAM_CODPRESTAMO, codigoPrestamo)
						.addValue(PARAM_ITEM_CODITEM, item.getCodItem());

				// Establecer parametros y ejecutar SP
				Map<String, Object> dataDetalle = llamarSPLineas
						.execute(parametrosDetalle);

				// Obteniendo el total de filas afectadas al ejecutar SP
				resultado += (Integer) dataDetalle.get(TOTAL_FILAS_AFECTADAS);

				// Actualizar Item de prestamo
				resultado += actualizarEstadoItem(item);
			}
		}

		return resultado > 0;
	}

	private int actualizarEstadoItem(Item item) {
		logger.info("actualizarEstadoItem");

		// Instanciando variable para llamado de SP
		SimpleJdbcCall llamarSP = new SimpleJdbcCall(jdbcTemplate)
				.withProcedureName(SP_ACTUALIZAR_ESTADO_ITEM);

		// Estableciendo parametros de entrada al SP
		SqlParameterSource parametros = new MapSqlParameterSource()
		        .addValue(PARAM_ITEM_CODITEM, item.getCodItem())
		        .addValue(PARAM_ITEM_ESTADO, item.getEstado());

		// Establecer parametros y ejecutar SP
		Map<String, Object> data = llamarSP.execute(parametros);

		// Obteniendo el total de filas afectadas al ejecutar SP
		int totalRegistrosAfectado = (Integer) data.get(TOTAL_FILAS_AFECTADAS);

		return totalRegistrosAfectado;
	}

	public int modificarPrestamo(Prestamo prestamo) {
		logger.info("modificarPrestamo");

		// Instanciando variable para llamado de SP
		SimpleJdbcCall llamarSP = new SimpleJdbcCall(jdbcTemplate)
				.withProcedureName(SP_MODIFICAR_PRESTAMO);

		// Estableciendo parametros de entrada al SP
		SqlParameterSource parametros = new MapSqlParameterSource()
				.addValue(PARAM_CODPRESTAMO, prestamo.getCodPrestamo())
				.addValue(PARAM_ESTADO, prestamo.getEstado())
				.addValue(PARAM_CODDISENIADOR, prestamo.getDiseniador() == null ? null : prestamo.getDiseniador().getCodDiseniador());
		

		// Establecer parametros y ejecutar SP
		Map<String, Object> data = llamarSP.execute(parametros);

		// Obteniendo el total de filas afectadas al ejecutar SP
		int totalRegistrosAfectado = (Integer) data.get(TOTAL_FILAS_AFECTADAS);

		return totalRegistrosAfectado;
	}
	
	public int actualizarPrestamo(Prestamo prestamo) {
		logger.info("actualizarPrestamo");

		// Instanciando variable para llamado de SP
		SimpleJdbcCall llamarSP = new SimpleJdbcCall(jdbcTemplate)
				.withProcedureName(SP_ACTUALIZAR_PRESTAMO);

		// Estableciendo parametros de entrada al SP
		SqlParameterSource parametros = new MapSqlParameterSource()
				.addValue(PARAM_CODPRESTAMO, prestamo.getCodPrestamo())
				.addValue(PARAM_ESTADO, prestamo.getEstado())
				.addValue(PARAM_PERIODOXDIAS, prestamo.getPeriodoxDias())
				.addValue(PARAM_FECHAFIN, prestamo.getFechaFin())
				.addValue(PARAM_CODDISENIADOR,prestamo.getDiseniador() == null ? null : prestamo.getDiseniador().getCodDiseniador());

		// Establecer parametros y ejecutar SP
		Map<String, Object> data = llamarSP.execute(parametros);

		// Obteniendo el total de filas afectadas al ejecutar SP
		int totalRegistrosAfectado = (Integer) data.get(TOTAL_FILAS_AFECTADAS);
		// ---------------------------------------------------------------------------
		// Obteniendo lista de item
		ListaItem listaItem = prestamo.getListaItem();

		// Si existen items entonces asociarlos
		if (listaItem != null && listaItem.getLista() != null) {

			for (Item item : listaItem.getLista()) {

				// Actualizar Item de prestamo
				totalRegistrosAfectado += actualizarEstadoItem(item);
			}
		}
		// -----------------------------------------------------------------------------
		return totalRegistrosAfectado;
	}
}
